-module(cus_redis).

-include("cus.hrl").
-include("logger.hrl").
%% API
-export([start/5]).

-export([
  get_conversation_info/1,
  get_conversation_ids/1,
  get_user_info/1,
  get_message/1,
  get_message_ids/1,
  set_conversation_info/1,
  set_user_info/1,
  set_message/1,
  set_sys_emotion_packets/1,
  get_sys_emotion_packets/0,
  get_sys_emotion_packet/1,
  flush_all/0,
  flush_db/0,
  get_value/1,
  get_values/2,
  set_value/2,
  keys/1
]).


-define(PROCNAME, 'cus_redis_').


start(Host, Port, DB, Pwd, Size) ->
  lists:foreach(fun(Index) ->
    case eredis:start_link(Host, Port, DB, Pwd) of
      {ok, Client} ->
        ProcName = get_proc_name(Index),
        register(ProcName, Client),
        {ok, Client};
      {error, _} = Err ->
        Err
    end
                end, lists:seq(1, Size)),
  lager:info("cus redis start success").



get_proc_name(Index) ->
  Name = erlang:atom_to_list(?PROCNAME) ++ erlang:integer_to_list(Index),
  erlang:list_to_atom(Name).


get_redis_proc(Key) ->
  Size = cus_config:redis_size(),
  Index = cus_util:phash(Key, Size),
  get_proc_name(Index).


get_conversation_info(Cid) when is_list(Cid) ->
  get_conversation_info(?l2b(Cid));
get_conversation_info(Cid) ->
  Key = <<"cus:conversation:", Cid/binary>>,
  to_term(get_value(Key)).


get_conversation_ids(Match) when is_list(Match) ->
  get_conversation_ids(?l2b(Match));
get_conversation_ids(Match) ->
  KeyMatch = <<"cus:conversation:", Match/binary>>,
  Keys = keys(KeyMatch),
  [Key -- "cus:conversation:" || Key <- Keys].


get_user_info(Uid) when is_list(Uid) ->
  get_user_info(?l2b(Uid));
get_user_info(Uid) ->
  Key = <<"cus:user:", Uid/binary>>,
  to_term(get_value(Key)).


get_message(Mid) when is_list(Mid) ->
  get_message(?l2b(Mid));
get_message(Mid) ->
  Key = <<"cus:message:", Mid/binary>>,
  to_term(get_value(Key)).

get_message_ids(Match) when is_list(Match) ->
  get_message_ids(?l2b(Match));
get_message_ids(Match) ->
  KeyMatch = <<"cus:message:", Match/binary>>,
  Keys = keys(KeyMatch),
  [Key -- "cus:message:" || Key <- Keys].


set_conversation_info(Info) ->
  Cid = ?l2b(Info#cus_conversation.cid),
  Key = <<"cus:conversation:", Cid/binary>>,
  set_value(Key, term_to_binary(Info)).


set_user_info(Info) ->
  Uid = ?l2b(Info#cus_user.uid),
  Key = <<"cus:user:", Uid/binary>>,
  set_value(Key, term_to_binary(Info)).


set_message(Info) ->
  Mid = ?l2b(Info#msg.mid),
  Key = <<"cus:message:", Mid/binary>>,
  set_value(Key, term_to_binary(Info)).



set_sys_emotion_packets(Info) ->
  Eid = ?l2b(Info#cus_sys_emotion_packet.id),
  Key = <<"cus:sys_emotion_packet:", Eid/binary>>,
  set_value(Key, term_to_binary(Info)).


get_sys_emotion_packets() ->
  Match = <<"cus:sys_emotion_packet:*">>,
  Keys = keys(Match),
  get_values(Keys, []).



get_sys_emotion_packet(ID) when is_list(ID) ->
  get_sys_emotion_packet(?l2b(ID));
get_sys_emotion_packet(ID) ->
  Key = <<"cus:sys_emotion_packet:", ID/binary>>,
  to_term(get_value(Key)).


get_value(Key) ->
  ProcName = get_redis_proc(Key),
  safe_q(ProcName, ["GET", Key]).


set_value(Key, Value) ->
  ProcName = get_redis_proc(Key),
  safe_q(ProcName, ["SET", Key, Value]).


get_values([], Acc) ->
  Acc;
get_values([Key | T], Acc) ->
  case to_term(get_value(Key)) of
    undefined ->
      get_values(T, Acc);
    Term ->
      get_values(T, [Term | Acc])
  end.



keys(Match) ->
  ProcName = get_redis_proc(Match),
  [?b2l(Item) || Item <- safe_q(ProcName, ["KEYS", Match])].


flush_all() ->
  safe_q('cus_redis_1', ["FLUSHALL"]).


flush_db() ->
  safe_q('cus_redis_1', ["FLUSHDB"]).


safe_q(ProcName, Cmd) ->
  case whereis(ProcName) of
    undefined ->
      [Host, Port, DB, Pwd, _Size] = cus_config:redis(),
      case eredis:start_link(?list(Host), Port, DB, ?list(Pwd)) of
        {ok, Client} ->
          register(ProcName, Client),
          case eredis:q(ProcName, Cmd) of
            {ok, undefined} ->
              <<>>;
            {ok, V} ->
              V;
            _Err -> <<>>
          end;
        {error, Err} ->
          lager:error("Failed to start redis client: ~p", [Err]),
          <<>>
      end;
    _ ->
      case eredis:q(ProcName, Cmd) of
        {ok, undefined} ->
          <<>>;
        {ok, V} ->
          V;
        _Err -> <<>>
      end
  end.


to_term(<<>>) ->
  undefined;
to_term(undefined) ->
  undefined;
to_term(Bin) ->
  binary_to_term(Bin).
